
#include <iostream>		// cout
#include <memory>		// share_ptr, weak_ptr, shared_from_this
#include <string>		// string
#include <mutex>		// mutex, lock_guard
#include <map>			// map
#include <functional>	// bind

class Stock
{
public:
    Stock(const std::string& s): price(0)
    {
        name = s;
        std::cout << "construct:" << name << std::endl;
    }
    std::string key() const
    {
        return name;
    }
    std::string name;
    double price;
    ~Stock()
    {
        std::cout << "destruct:" << name << std::endl;
    }
};

class StockFactory : public std::enable_shared_from_this<StockFactory>
{
public:
    std::shared_ptr<Stock> get(const std::string& key)
    {
        std::lock_guard<std::mutex> lock(mutex_);
        std::weak_ptr<Stock>& wkStock = stocks_[key];
        std::shared_ptr<Stock> pStock = wkStock.lock();
        if (!pStock)
        {
            pStock.reset(new Stock(key),
                std::bind(&StockFactory::weakDeleteCallback,
                    std::weak_ptr<StockFactory>(shared_from_this()),
                    std::placeholders::_1));
            wkStock = pStock;  // must write back to map !!!
        }
        return pStock;
    }

    void print_info() const
    {
        std::cout << "count: " << stocks_.size() << std::endl;
        for (auto iter = stocks_.begin(); iter != stocks_.end(); ++iter) 
        {
            std::shared_ptr<Stock> pStock = iter->second.lock();
            if (pStock)
            {
                std::cout << "key: " << iter->first << " , " 
                          << "name: " << pStock->name << " , "
                          << "price: " << pStock->price<< std::endl;
            }
        }

    }

private:
    std::mutex mutex_;
    std::map<std::string, std::weak_ptr<Stock>> stocks_;

private:
    static void weakDeleteCallback(const std::weak_ptr<StockFactory>& wkFactory,
        Stock* stock)
    {
        std::shared_ptr<StockFactory> factory = wkFactory.lock();
        if (factory)
        {
            factory->removeStock(stock);
        }
        delete stock;
    }
    void removeStock(Stock* stock)
    {
        if (stock)
        {
            std::lock_guard<std::mutex> lock(mutex_);
            stocks_.erase(stock->key());
        }
    }
};

void test1(void)
{
    std::cout << "test1 begin" << std::endl;
    std::shared_ptr<StockFactory> factory(new StockFactory);
    {
        std::shared_ptr<Stock> stock1 = factory->get("ZHY");
        std::shared_ptr<Stock> stock2 = factory->get("ZHY");
        factory->print_info();
        std::shared_ptr<Stock> stock3 = factory->get("CMD");
        factory->print_info();
    }
    factory->print_info();
    std::cout << "test1 end" << std::endl;
}

void test2(void)
{
    std::cout << "test2 begin" << std::endl;
    std::shared_ptr<Stock> stock;
    {
        std::shared_ptr<StockFactory> factory(new StockFactory);
        stock = factory->get("ZHY");
        factory->print_info();
    }
    if (stock)
    {
        std::cout << "name: " << stock->name << " , "
                  << "price: " << stock->price<< std::endl;
    }
    std::cout << "test2 end" << std::endl;
}

int main(int c, char* argv[])
{
    test1();
    test2();
}
